<?php

namespace App\Helpers;

/**
 * 验证数据完整性
 * @package App\Helpers
 */
class ValidationHelper {

    #region 内置验证类型

    /**
     * 不能为空
     */
    CONST FILTER_REQUIRED = "required";

    /**
     * 电子邮箱
     */
    CONST FILTER_EMAIL = "email";

    /**
     * URL
     */
    CONST FILTER_URL = "url";

    /**
     * 日期
     */
    CONST FILTER_DATE = "date";

    /**
     * 日期 (ISO)
     */
    CONST FILTER_DATE_ISO = "dateISO";

    /**
     * 数字
     */
    CONST FILTER_NUMBER = "number";

    /**
     * 整数
     */
    CONST FILTER_DIGITS = "digits";

    /**
     * 相同的值
     */
    CONST FILTER_EQUAL_TO = "equalTo";

    /**
     * 不包含Html标签及单引号的字符串
     */
    CONST FILTER_LETTER = "letter";

    /**
     * 英文或者数字字符
     */
    CONST FILTER_ENG = "eng";

    /**
     * 中文字符
     */
    CONST FILTER_CHN = "chn";

    /**
     * 联系电话（包含手机号码）
     */
    CONST FILTER_PHONE = "phone";

    /**
     * 手机号
     */
    CONST FILTER_MOBILE = "mobile";

    /**
     * 最大字符长度
     */
    CONST FILTER_MAX_LENGTH = "maxLength";

    /**
     * 最小字符长度
     */
    CONST FILTER_MIN_LENGTH = "minLength";

    /**
     * 介于指定长度之间的字符
     */
    CONST FILTER_RANGE_LENGTH = "rangeLength";

    /**
     * 介于指定区间的值
     */
    CONST FILTER_RANGE = "range";

    /**
     * 最大值
     */
    CONST FILTER_MAX = "max";

    /**
     * 最小值
     */
    CONST FILTER_MIN = "min";

    /**
     * 在指定的列表中
     */
    CONST FILTER_IN = "in";

    #endregion

    #region 验证方法

    /**
     * 验证字符串参数是否为空
     * @param $data 要验证的数据
     * @return boolean
     */
    function required($data) {
        if ((is_array($data) && !empty($data)) || (!is_array($data) && strlen(trim($data))))
            return true;
        else
            return false;
    }

    /**
     * 验证电子邮箱格式
     * @param $data 要验证的电子邮箱
     * @return boolean
     */
    function email($data) {
        return !$this->required($data) || preg_match("/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i", $data);
    }

    /**
     * 验证URL格式
     * @param $data 要验证的URL
     * @return boolean
     */
    function url($data) {
        return !$this->required($data) || preg_match('/(http|https|ftp|file){1}(:\/\/)?([\da-z-\.]+)\.([a-z]{2,6})([\/\w \.-?&%-=]*)*\/?/', $data);
    }

    /**
     *
     * 验证日期格式
     * @param $data 要验证的日期
     * @return boolean
     */
    function date($data) {
        return !$this->required($data) || (date($data) ? true : false);
    }

    /**
     * 验证日期 (ISO)格式
     * @param $data 要验证的日期 (ISO)
     * @return boolean
     */
    function dateISO($data) {
        return !$this->required($data) || preg_match("/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/", $data);
    }

    /**
     * 验证是否是数字
     * @param $data 要验证的数字
     * @return boolean
     */
    function number($data) {
        return !$this->required($data) || preg_match("/^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/", $data);
    }

    /**
     * 验证是否是整数
     * @param $data 要验证的数字
     * @return boolean
     */
    function digits($data) {
        return !$this->required($data) || preg_match("/^\d+$/", $data);
    }

    /**
     * 验证是否是和指定的值相同
     * @param $data 要验证的值
     * @param $element 比较的值
     * @return boolean
     */
    function equalTo($data, $element) {
        return !$this->required($data) || $data === $element;
    }

    /**
     * 验证字符串是否包含Html标签及单引号
     * @param $data 要验证的值
     * @return boolean
     */
    function letter($data) {
        return !$this->required($data) || !preg_match("[']+", $data) && !preg_match("<(.*)>.*|<(.*)/>", $data);
    }

    /**
     * 验证字符串是否全是英文或者数字字符
     * @param $data 要验证的值
     * @return boolean
     */
    function eng($data) {
        return !$this->required($data) || preg_match("/^[A-Za-z0-9]+$/", $data);
    }

    /**
     * 验证字符串是否全是中文字符
     * @param $data 要验证的值
     * @return boolean
     */
    function chn($data) {
        return !$this->required($data) || preg_match("/^[\u4E00-\u9FA5]$/", $data);
    }

    /**
     * 验证联系电话（包含手机号码）格式
     * @param $data 要验证的电话号码
     * @return boolean
     */
    function phone($data) {
        return !$this->required($data) || (preg_match("/^(13|15|18|17)\d{9}$/", $data) || preg_match("/^(013|015|018)\d{9}$/", $data) ||
                preg_match("/^[0-9]{7,8}$/", $data) || preg_match("/^[0-9]{3,4}[-]{1}[0-9]{7,8}[-]{1}[0-9]{2,4}$/", $data) ||
                preg_match("/^[0-9]{3,4}[-]{0,1}[0-9]{7,8}$/", $data) || preg_match("/^[0-9]{7,8}[-]{1}[0-9]{2,4}$/", $data) ||
                preg_match("/^(400|800)[-]{1}[0-9]{3}[-]{1}[0-9]{4}$/", $data) || preg_match("/^(400|800)[0-9]{7}$/", $data));
    }

    /**
     * 验证手机号格式
     * @param $data 要验证的手机号
     * @return boolean
     */
    function mobile($data) {
        return !$this->required($data) || preg_match("/^(13|15|16|18|17)\d{9}$/", $data) || preg_match("/^(013|015|018)\d{9}$/", $data);
    }

    /**
     * 验证最大长度
     * @param $data 要验证的值
     * @param $maxLength 最大长度
     * @return boolean
     */
    function maxLength($data, $maxLength) {
        return !$this->required($data) || is_array($data) && count($data) <= $maxLength || strlen($data) <= $maxLength;
    }

    /**
     * 验证最小长度
     * @param $data 要验证的值
     * @param $minLength 最小长度
     * @return boolean
     */
    function minLength($data, $minLength) {
        return !$this->required($data) || ((is_array($data) && count($data) >= $minLength) || strlen($data) >= $minLength);
    }

    /**
     * 验证介于指定长度之间的值
     * @param $data 要验证的值
     * @param $rangeLength 长度区间
     * @return boolean
     */
    function rangeLength($data, $rangeLength) {
        if (is_array($data)) {
            $length = count($data);
        } else {
            $length = strlen($data);
        }
        return !$this->required($data) || ($length >= $rangeLength[0] && $length <= $rangeLength[1]);
    }

    /**
     * 验证介于指定区间的值
     * @param $data 要验证的值
     * @param $range 值区间
     * @return boolean
     */
    function range($data, $range) {
        return !$this->required($data) || ($data >= $range[0] && $data <= $range[1]);
    }

    /**
     * 验证最大值
     * @param $data 要验证的值
     * @param $max 最大值
     * @return boolean
     */
    function max($data, $max) {
        return !$this->required($data) || $data <= $max;
    }

    /**
     * 验证最小值
     * @param $data 要验证的值
     * @param $min 最小值
     * @return boolean
     */
    function min($data, $min) {
        return !$this->required($data) || $data >= $min;
    }

    /**
     * 验证值是否在指定的列表中
     * @param $data 要验证的值
     * @param $in 指定的值列表
     * @return boolean
     */
    function in($data, $in) {
        return !$this->required($data) || ((is_array($in) && in_array($data, $in)) || $data == $in);
    }

    #endregion

    #region 验证错误消息设置

    /**
     * 错误消息 数组
     * @var array
     */
    private static $messages = array(
        /**
         * 必须参数
         */
        "required" => "%s不能为空",

        /**
         * 电子邮箱
         */
        "email" => "%s的格式不正确",

        /**
         * URL
         */
        "url" => "%s的格式不正确",

        /**
         * 日期
         */
        "date" => "%s的格式不正确",

        /**
         * 日期 (ISO)
         */
        "dateISO" => "%s的格式不正确",

        /**
         * 数字
         */
        "number" => "%s不是数字",

        /**
         * 整数
         */
        "digits" => "%s不是整数",

        /**
         * 相同的值
         */
        "equalTo" => "%s两次的值不一致",

        /**
         * 不包含Html标签及单引号的字符串
         */
        "letter" => "%s包含Html标签或者单引号",

        /**
         * 英文或者数字字符
         */
        "eng" => "%s包含英文和数字以外的字符",

        /**
         * 中文字符
         */
        "chn" => "%s包含中文以外的字符",

        /**
         * 联系电话（包含手机号码）
         */
        "phone" => "%s的格式不正确",

        /**
         * 手机号
         */
        "mobile" => "%s的格式不正确",

        /**
         * 最大字符长度
         */
        "maxLength" => "%s最大长度不能超过%s个字符",

        /**
         * 最小字符长度
         */
        "minLength" => "%s最小长度不能小于%s个字符",

        /**
         * 介于指定长度之间的字符
         */
        "rangeLength" => "%s的长度必须介于%s和%s之间",

        /**
         * 介于指定区间的值
         */
        "range" => "%s的值必须介于%s和%s之间",

        /**
         * 最大值
         */
        "max" => "%s的值不能大于%s",

        /**
         * 最小值
         */
        "min" => "%s的值不能小于于%s%",

        /**
         * 在指定的列表中
         */
        "in" => "%s的值不在指定的值列表中",
    );

    /**
     * 获取错误消息
     * @param $type 验证类型
     * @param $name 数据名称
     * @param $params 参数数组
     * @return string
     */
    function getErrorMessage($type, $name, $params = null) {
        $message = self::$messages[$type];
        if (!$params) {
            $params[] = $name;
        } else {
            array_unshift($params, $name);
        }
        return vsprintf($message, $params);
    }

    #endregion
}